import EventEmitter from "../eventEmitter";
import RectNodeGraph from "../shape/rect";
import GraphBase from "../shape/graphBase";
import { PointControllerAzimuthEnum } from "../enum";
import { throttle } from "../utils";
import { getScale } from "@/graphviewState";
export default class RectControlPoints extends EventEmitter {
  target: HTMLCanvasElement;
  ctx: CanvasRenderingContext2D | null;
  // 记录所有的控制点
  pointMap = new Map();
  pointSize = 10;
  pointColor = "rgb(200,200,200)";
  // 当前鼠标悬停的控制点
  hoverControlPoint!: {
    azimuth: string;
    target: RectNodeGraph;
  } | null;
  // 是否左键按下
  isLeftKeyDown = false;
  // 控制点控制的图元对象
  controlTarget: GraphBase[] | null | undefined;
  mouseStartPosition: { x: number; y: number } = { x: 0, y: 0 };
  // 当前控制器是否处于激活状态
  activity = false;
  constructor(target: HTMLCanvasElement) {
    super();
    this.target = target;
    this.ctx = target.getContext("2d");
    this.init();
  }
  init() {
    const pointAzimuth = [
      PointControllerAzimuthEnum.LEFTTOP,
      PointControllerAzimuthEnum.MIDDLETOP,
      PointControllerAzimuthEnum.RIGHTTOP,
      PointControllerAzimuthEnum.LEFT,
      PointControllerAzimuthEnum.RIGHT,
      PointControllerAzimuthEnum.LEFTBOTTOM,
      PointControllerAzimuthEnum.MIDDLEBOTTOM,
      PointControllerAzimuthEnum.RIGHTBOTTOM,
    ];
    pointAzimuth.forEach((key) => {
      this.pointMap.set(key, new RectNodeGraph());
    });
    if (this.ctx) {
      this.pointMap.forEach((point: RectNodeGraph) => {
        point.setCtx(this.ctx);
        point.setWidth(this.pointSize);
        point.setHeight(this.pointSize);
        point.setBackgroundColor(this.pointColor);
      });
    }
    this.initInteractor();
  }
  setCursor(cursorStyle: string) {
    this.target.style.cursor = cursorStyle;
  }
  setControlTarget(target: GraphBase[] | null | undefined) {
    this.controlTarget = target;
  }
  setActivity(activity: boolean) {
    this.activity = activity;
  }
  destroy() {
    this.target.removeEventListener("mousemove", this.handleMouseMove);
    this.target.removeEventListener("mousedown", this.handleMouseDown);
    this.target.removeEventListener("mouseup", this.handleMouseUp);
    this.clear();
  }
  initInteractor() {
    this.handleMouseMove = throttle(this.handleMouseMove, 16, this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.target.addEventListener("mousedown", this.handleMouseDown);
    this.target.addEventListener("mouseup", this.handleMouseUp);
    this.target.addEventListener("mousemove", this.handleMouseMove);
  }
  handleMouseDown(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 去除缩放对鼠标获取点坐标的影响
    const scale = getScale();
    this.mouseStartPosition.x = e.offsetX / scale;
    this.mouseStartPosition.y = e.offsetY / scale;
    this.isLeftKeyDown = true;
    this.dragStart();
  }
  handleMouseUp(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    this.dragEnd();
    this.mouseStartPosition.x = 0;
    this.mouseStartPosition.y = 0;
    this.isLeftKeyDown = false;
  }
  handleMouseMove(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 去除缩放对鼠标获取点坐标的影响
    const scale = getScale();
    const x = e.offsetX / scale;
    const y = e.offsetY / scale;
    if (this.isLeftKeyDown && this.hoverControlPoint) {
      this.onResize(x, y);
      return;
    }
    if (
      this.isLeftKeyDown &&
      !this.hoverControlPoint &&
      Array.isArray(this.controlTarget)
    ) {
      this.drag(x - this.mouseStartPosition.x, y - this.mouseStartPosition.y);
      return;
    }
    const hoverRes = this.findControlPointsHover(x, y);
    if (hoverRes) {
      this.hoverControlPoint = hoverRes;
      switch (hoverRes.azimuth) {
        case PointControllerAzimuthEnum.LEFT:
        case PointControllerAzimuthEnum.RIGHT:
          this.setCursor("w-resize");
          break;
        case PointControllerAzimuthEnum.MIDDLETOP:
        case PointControllerAzimuthEnum.MIDDLEBOTTOM:
          this.setCursor("s-resize");
          break;
        case PointControllerAzimuthEnum.LEFTTOP:
        case PointControllerAzimuthEnum.RIGHTBOTTOM:
          this.setCursor("se-resize");
          break;
        case PointControllerAzimuthEnum.LEFTBOTTOM:
        case PointControllerAzimuthEnum.RIGHTTOP:
          this.setCursor("ne-resize");
          break;
        default:
          this.setCursor("pointer");
          break;
      }
    } else {
      this.hoverControlPoint = null;
    }
  }
  getHoverPoint() {
    return this.hoverControlPoint?.target;
  }
  // 更新控制点的位置
  updateControlPoints(rect: {
    x: number;
    y: number;
    width: number;
    height: number;
  }) {
    const leftTopX = rect.x;
    const leftTopY = rect.y;
    const width = rect.width;
    const height = rect.height;
    const rightBottomX = leftTopX + width;
    const rightBottomY = leftTopY + height;
    this.pointMap
      .get("leftTop")
      .setPosition(
        leftTopX - this.pointSize / 2,
        leftTopY - this.pointSize / 2
      );
    this.pointMap
      .get("middleTop")
      .setPosition(
        (leftTopX + rightBottomX) / 2,
        leftTopY - this.pointSize / 2
      );
    this.pointMap
      .get("rightTop")
      .setPosition(
        rightBottomX + this.pointSize / 2,
        leftTopY - this.pointSize / 2
      );
    this.pointMap
      .get("left")
      .setPosition(
        leftTopX - this.pointSize / 2,
        (leftTopY + rightBottomY) / 2
      );
    this.pointMap
      .get("right")
      .setPosition(
        rightBottomX + this.pointSize / 2,
        (leftTopY + rightBottomY) / 2
      );
    this.pointMap
      .get("leftBottom")
      .setPosition(
        leftTopX - this.pointSize / 2,
        rightBottomY + this.pointSize / 2
      );
    this.pointMap
      .get("middleBottom")
      .setPosition(
        (leftTopX + rightBottomX) / 2,
        rightBottomY + this.pointSize / 2
      );
    this.pointMap
      .get("rightBottom")
      .setPosition(
        rightBottomX + this.pointSize / 2,
        rightBottomY + this.pointSize / 2
      );
    this.drawControlPoints();
  }
  // 重绘控制点
  drawControlPoints() {
    if (!this.activity) {
      return;
    }
    if (this.ctx) {
      const scale = getScale();
      this.ctx.save();
      this.ctx.scale(scale, scale);
      this.pointMap.forEach((point: RectNodeGraph) => {
        point.draw();
      });
      this.ctx.restore();
    }
  }
  // 判断鼠标是否悬浮在控制点上
  findControlPointsHover(
    x: number,
    y: number
  ): null | { azimuth: string; target: RectNodeGraph } {
    let result = null;
    this.pointMap.forEach((point: RectNodeGraph, azimuth: string) => {
      if (point.contain(x, y)) {
        result = {
          azimuth: azimuth,
          target: point,
        };
      }
    });
    return result;
  }
  dragStart() {}
  // 拖拽图元执行
  drag(translateX: number, translateY: number) {
    this.emit("drag", [translateX, translateY]);
    this.emit("redraw");
    this.drawControlPoints();
  }
  dragEnd() {
    if (this.isLeftKeyDown && Array.isArray(this.controlTarget)) {
      this.emit("drop");
    }
  }
  // 拖拽控制点变化位置尺寸时执行
  onResize(x: number, y: number) {
    if (!this.hoverControlPoint) {
      return;
    }
    this.hoverControlPoint.target.setPosition(x, y);
    switch (this.hoverControlPoint.azimuth) {
      case PointControllerAzimuthEnum.LEFT:
      case PointControllerAzimuthEnum.RIGHT:
        this.onWResize();
        break;
      case PointControllerAzimuthEnum.MIDDLETOP:
      case PointControllerAzimuthEnum.MIDDLEBOTTOM:
        this.onSResize();
        break;
      case PointControllerAzimuthEnum.LEFTTOP:
      case PointControllerAzimuthEnum.RIGHTBOTTOM:
        this.onSEResize();
        break;
      case PointControllerAzimuthEnum.LEFTBOTTOM:
      case PointControllerAzimuthEnum.RIGHTTOP:
        this.onNEResize();
        break;
      default:
        break;
    }
    this.emit("redraw");
    this.drawControlPoints();
  }
  // 左上和右下控制点被拖拽
  onSEResize() {
    const rect = {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
    };
    const leftTopPoint = this.pointMap.get(PointControllerAzimuthEnum.LEFTTOP);
    const rightBottomPoint = this.pointMap.get(
      PointControllerAzimuthEnum.RIGHTBOTTOM
    );
    rect.x = (leftTopPoint as RectNodeGraph).position.x + this.pointSize / 2;
    rect.y = (leftTopPoint as RectNodeGraph).position.y + this.pointSize / 2;
    rect.width =
      (rightBottomPoint as RectNodeGraph).position.x -
      this.pointSize / 2 -
      rect.x;
    rect.height =
      (rightBottomPoint as RectNodeGraph).position.y -
      this.pointSize / 2 -
      rect.y;
    this.updateControlPoints(rect);
    this.emit("resize", rect);
  }
  // 左右控制点被拖拽
  onWResize() {
    const rect = {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
    };
    const leftPoint = this.pointMap.get(PointControllerAzimuthEnum.LEFT);
    const leftTopPoint = this.pointMap.get(PointControllerAzimuthEnum.LEFTTOP);
    const rightPoint = this.pointMap.get(PointControllerAzimuthEnum.RIGHT);
    const rightBottomPoint = this.pointMap.get(
      PointControllerAzimuthEnum.RIGHTBOTTOM
    );
    rect.x = (leftPoint as RectNodeGraph).position.x + this.pointSize / 2;
    rect.y = (leftTopPoint as RectNodeGraph).position.y + this.pointSize / 2;
    rect.width =
      (rightPoint as RectNodeGraph).position.x - this.pointSize / 2 - rect.x;
    rect.height =
      (rightBottomPoint as RectNodeGraph).position.y -
      this.pointSize / 2 -
      rect.y;
    this.updateControlPoints(rect);
    this.emit("resize", rect);
  }
  // 中上和中下控制点拖拽
  onSResize() {
    const rect = {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
    };
    const middleTopPoint = this.pointMap.get(
      PointControllerAzimuthEnum.MIDDLETOP
    );
    const leftTopPoint = this.pointMap.get(PointControllerAzimuthEnum.LEFTTOP);
    const middleBottomPoint = this.pointMap.get(
      PointControllerAzimuthEnum.MIDDLEBOTTOM
    );
    const rightBottomPoint = this.pointMap.get(
      PointControllerAzimuthEnum.RIGHTBOTTOM
    );
    rect.x = (leftTopPoint as RectNodeGraph).position.x + this.pointSize / 2;
    rect.y = (middleTopPoint as RectNodeGraph).position.y + this.pointSize / 2;
    rect.width =
      (rightBottomPoint as RectNodeGraph).position.x -
      this.pointSize / 2 -
      rect.x;
    rect.height =
      (middleBottomPoint as RectNodeGraph).position.y -
      this.pointSize / 2 -
      rect.y;
    this.updateControlPoints(rect);
    this.emit("resize", rect);
  }
  // 右上和左下控制点拖拽
  onNEResize() {
    const rect = {
      x: 0,
      y: 0,
      width: 0,
      height: 0,
    };
    const leftBottomPoint = this.pointMap.get(
      PointControllerAzimuthEnum.LEFTBOTTOM
    );
    const rightTopPoint = this.pointMap.get(
      PointControllerAzimuthEnum.RIGHTTOP
    );
    rect.x = (leftBottomPoint as RectNodeGraph).position.x + this.pointSize / 2;
    rect.y = (rightTopPoint as RectNodeGraph).position.y + this.pointSize / 2;
    rect.width =
      (rightTopPoint as RectNodeGraph).position.x - this.pointSize / 2 - rect.x;
    rect.height =
      (leftBottomPoint as RectNodeGraph).position.y -
      this.pointSize / 2 -
      rect.y;
    this.updateControlPoints(rect);
    this.emit("resize", rect);
  }
}
